﻿//#define FEATURE_THREAD_THREADPOOL 
//using System;
//using System.Collections.Generic;
//using System.Linq;
//using System.Text;
//using System.Threading.Tasks;
//using System.IO;
//using System.Threading;
//using Renci.SshNet;
//using Renci.SshNet.Channels;
//using Renci.SshNet.Common;
//using Renci.SshNet.Messages.Connection;
//using Renci.SshNet.Messages.Transport;
//using System.Globalization;
//using Renci.SshNet.Abstractions;

//namespace Bitmanager.ImportPipeline.StreamProviders
//{
//      /// <summary>
//      /// Represents SSH command that can be executed.
//      /// </summary>
//      public class SshCommandRaw : IDisposable
//      {
//         private ISession _session;
//         private readonly Encoding _encoding;
//         private IChannelSession _channel;
//         private CommandAsyncResult _asyncResult;
//         private AsyncCallback _callback;
//         private EventWaitHandle _sessionErrorOccuredWaitHandle;
//         private Exception _exception;
//         private bool _hasError;
//         private readonly object _endExecuteLock = new object();

//         /// <summary>
//         /// Gets the command text.
//         /// </summary>
//         public string CommandText { get; private set; }

//         /// <summary>
//         /// Gets or sets the command timeout.
//         /// </summary>
//         /// <value>
//         /// The command timeout.
//         /// </value>
//         /// <example>
//         ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\SshCommandTest.cs" region="Example SshCommand CreateCommand Execute CommandTimeout" language="C#" title="Specify command execution timeout" />
//         /// </example>
//         public TimeSpan CommandTimeout { get; set; }

//         /// <summary>
//         /// Gets the command exit status.
//         /// </summary>
//         /// <example>
//         ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\SshCommandTest.cs" region="Example SshCommand RunCommand ExitStatus" language="C#" title="Get command execution exit status" />
//         /// </example>
//         public int ExitStatus { get; private set; }

//         /// <summary>
//         /// Gets the output stream.
//         /// </summary>
//         /// <example>
//         ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\SshCommandTest.cs" region="Example SshCommand CreateCommand Execute OutputStream" language="C#" title="Use OutputStream to get command execution output" />
//         /// </example>
//         public Stream OutputStream { get; private set; }

//         /// <summary>
//         /// Gets the extended output stream.
//         /// </summary>
//         /// <example>
//         ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\SshCommandTest.cs" region="Example SshCommand CreateCommand Execute ExtendedOutputStream" language="C#" title="Use ExtendedOutputStream to get command debug execution output" />
//         /// </example>
//         public Stream ExtendedOutputStream { get; private set; }

//         private StringBuilder _result;
//         /// <summary>
//         /// Gets the command execution result.
//         /// </summary>
//         /// <example>
//         ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\SshCommandTest.cs" region="Example SshCommand RunCommand Result" language="C#" title="Running simple command" />
//         /// </example>
//         public string Result
//         {
//            get
//            {
//               if (_result == null)
//               {
//                  _result = new StringBuilder();
//               }

//               if (OutputStream != null && OutputStream.Length > 0)
//               {
//                  // do not dispose the StreamReader, as it would also dispose the stream
//                  var sr = new StreamReader(OutputStream, _encoding);
//                  _result.Append(sr.ReadToEnd());
//               }

//               return _result.ToString();
//            }
//         }

//         private StringBuilder _error;
//         /// <summary>
//         /// Gets the command execution error.
//         /// </summary>
//         /// <example>
//         ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\SshCommandTest.cs" region="Example SshCommand CreateCommand Error" language="C#" title="Display command execution error" />
//         /// </example>
//         public string Error
//         {
//            get
//            {
//               if (_hasError)
//               {
//                  if (_error == null)
//                  {
//                     _error = new StringBuilder();
//                  }

//                  if (ExtendedOutputStream != null && ExtendedOutputStream.Length > 0)
//                  {
//                     // do not dispose the StreamReader, as it would also dispose the stream
//                     var sr = new StreamReader(ExtendedOutputStream, _encoding);
//                     _error.Append(sr.ReadToEnd());
//                  }

//                  return _error.ToString();
//               }
//               return string.Empty;
//            }
//         }

//         /// <summary>
//         /// Initializes a new instance of the <see cref="SshCommand"/> class.
//         /// </summary>
//         /// <param name="session">The session.</param>
//         /// <param name="commandText">The command text.</param>
//         /// <param name="encoding">The encoding to use for the results.</param>
//         /// <exception cref="ArgumentNullException">Either <paramref name="session"/>, <paramref name="commandText"/> is <c>null</c>.</exception>
//         public SshCommandRaw(ISession session, string commandText, Encoding encoding)
//         {
//            if (session == null)
//               throw new ArgumentNullException("session");
//            if (commandText == null)
//               throw new ArgumentNullException("commandText");
//            if (encoding == null)
//               throw new ArgumentNullException("encoding");

//            _session = session;
//            CommandText = commandText;
//            _encoding = encoding;
//            CommandTimeout = Session.InfiniteTimeSpan;
//            _sessionErrorOccuredWaitHandle = new AutoResetEvent(false);

//            _session.Disconnected += Session_Disconnected;
//            _session.ErrorOccured += Session_ErrorOccured;
//         }

//         /// <summary>
//         /// Begins an asynchronous command execution.
//         /// </summary>
//         /// <returns>
//         /// An <see cref="System.IAsyncResult" /> that represents the asynchronous command execution, which could still be pending.
//         /// </returns>
//         /// <example>
//         ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\SshCommandTest.cs" region="Example SshCommand CreateCommand BeginExecute IsCompleted EndExecute" language="C#" title="Asynchronous Command Execution" />
//         /// </example>
//         /// <exception cref="InvalidOperationException">Asynchronous operation is already in progress.</exception>
//         /// <exception cref="SshException">Invalid operation.</exception>
//         /// <exception cref="ArgumentException">CommandText property is empty.</exception>
//         /// <exception cref="SshConnectionException">Client is not connected.</exception>
//         /// <exception cref="SshOperationTimeoutException">Operation has timed out.</exception>
//         /// <exception cref="InvalidOperationException">Asynchronous operation is already in progress.</exception>
//         /// <exception cref="ArgumentException">CommandText property is empty.</exception>
//         public IAsyncResult BeginExecute()
//         {
//            return BeginExecute(null, null);
//         }

//         /// <summary>
//         /// Begins an asynchronous command execution.
//         /// </summary>
//         /// <param name="callback">An optional asynchronous callback, to be called when the command execution is complete.</param>
//         /// <returns>
//         /// An <see cref="System.IAsyncResult" /> that represents the asynchronous command execution, which could still be pending.
//         /// </returns>
//         /// <exception cref="InvalidOperationException">Asynchronous operation is already in progress.</exception>
//         /// <exception cref="SshException">Invalid operation.</exception>
//         /// <exception cref="ArgumentException">CommandText property is empty.</exception>
//         /// <exception cref="SshConnectionException">Client is not connected.</exception>
//         /// <exception cref="SshOperationTimeoutException">Operation has timed out.</exception>
//         /// <exception cref="InvalidOperationException">Asynchronous operation is already in progress.</exception>
//         /// <exception cref="ArgumentException">CommandText property is empty.</exception>
//         public IAsyncResult BeginExecute(AsyncCallback callback)
//         {
//            return BeginExecute(callback, null);
//         }

//         /// <summary>
//         /// Begins an asynchronous command execution.
//         /// </summary>
//         /// <param name="callback">An optional asynchronous callback, to be called when the command execution is complete.</param>
//         /// <param name="state">A user-provided object that distinguishes this particular asynchronous read request from other requests.</param>
//         /// <returns>
//         /// An <see cref="IAsyncResult" /> that represents the asynchronous command execution, which could still be pending.
//         /// </returns>
//         /// <exception cref="InvalidOperationException">Asynchronous operation is already in progress.</exception>
//         /// <exception cref="SshException">Invalid operation.</exception>
//         /// <exception cref="ArgumentException">CommandText property is empty.</exception>
//         /// <exception cref="SshConnectionException">Client is not connected.</exception>
//         /// <exception cref="SshOperationTimeoutException">Operation has timed out.</exception>
//         /// <exception cref="InvalidOperationException">Asynchronous operation is already in progress.</exception>
//         /// <exception cref="ArgumentException">CommandText property is empty.</exception>
//         public IAsyncResult BeginExecute(AsyncCallback callback, object state)
//         {
//            //  Prevent from executing BeginExecute before calling EndExecute
//            if (_asyncResult != null && !_asyncResult.EndCalled)
//            {
//               throw new InvalidOperationException("Asynchronous operation is already in progress.");
//            }

//            //  Create new AsyncResult object
//            _asyncResult = new CommandAsyncResult
//            {
//               AsyncWaitHandle = new ManualResetEvent(false),
//               IsCompleted = false,
//               AsyncState = state,
//            };

//            //  When command re-executed again, create a new channel
//            if (_channel != null)
//            {
//               throw new SshException("Invalid operation.");
//            }

//            if (string.IsNullOrEmpty(CommandText))
//               throw new ArgumentException("CommandText property is empty.");

//            var outputStream = OutputStream;
//            if (outputStream != null)
//            {
//               outputStream.Dispose();
//               OutputStream = null;
//            }

//            var extendedOutputStream = ExtendedOutputStream;
//            if (extendedOutputStream != null)
//            {
//               extendedOutputStream.Dispose();
//               ExtendedOutputStream = null;
//            }

//            //  Initialize output streams
//            OutputStream = new PipeStream();
//            ExtendedOutputStream = new PipeStream();

//            _result = null;
//            _error = null;
//            _callback = callback;

//            _channel = CreateChannel();
//            _channel.Open();
//            _channel.SendExecRequest(CommandText);

//            return _asyncResult;
//         }

//         /// <summary>
//         /// Begins an asynchronous command execution.
//         /// </summary>
//         /// <param name="commandText">The command text.</param>
//         /// <param name="callback">An optional asynchronous callback, to be called when the command execution is complete.</param>
//         /// <param name="state">A user-provided object that distinguishes this particular asynchronous read request from other requests.</param>
//         /// <returns>
//         /// An <see cref="System.IAsyncResult" /> that represents the asynchronous command execution, which could still be pending.
//         /// </returns>
//         /// <exception cref="Renci.SshNet.Common.SshConnectionException">Client is not connected.</exception>
//         /// <exception cref="Renci.SshNet.Common.SshOperationTimeoutException">Operation has timed out.</exception>
//         public IAsyncResult BeginExecute(string commandText, AsyncCallback callback, object state)
//         {
//            CommandText = commandText;

//            return BeginExecute(callback, state);
//         }

//         /// <summary>
//         /// Waits for the pending asynchronous command execution to complete.
//         /// </summary>
//         /// <param name="asyncResult">The reference to the pending asynchronous request to finish.</param>
//         /// <returns>Command execution result.</returns>
//         /// <example>
//         ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\SshCommandTest.cs" region="Example SshCommand CreateCommand BeginExecute IsCompleted EndExecute" language="C#" title="Asynchronous Command Execution" />
//         /// </example>
//         /// <exception cref="ArgumentException">Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult.</exception>
//         /// <exception cref="ArgumentNullException"><paramref name="asyncResult"/> is <c>null</c>.</exception>
//         public string EndExecute(IAsyncResult asyncResult)
//         {
//            if (asyncResult == null)
//            {
//               throw new ArgumentNullException("asyncResult");
//            }

//            var commandAsyncResult = asyncResult as CommandAsyncResult;
//            if (commandAsyncResult == null || _asyncResult != commandAsyncResult)
//            {
//               throw new ArgumentException(string.Format("The {0} object was not returned from the corresponding asynchronous method on this class.", typeof(IAsyncResult).Name));
//            }

//            lock (_endExecuteLock)
//            {
//               if (commandAsyncResult.EndCalled)
//               {
//                  throw new ArgumentException("EndExecute can only be called once for each asynchronous operation.");
//               }

//               //  wait for operation to complete (or time out)
//               WaitOnHandle(_asyncResult.AsyncWaitHandle);

//               UnsubscribeFromEventsAndDisposeChannel(_channel);
//               _channel = null;

//               commandAsyncResult.EndCalled = true;

//               return Result;
//            }
//         }

//         /// <summary>
//         /// Executes command specified by <see cref="CommandText"/> property.
//         /// </summary>
//         /// <returns>Command execution result</returns>
//         /// <example>
//         ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\SshCommandTest.cs" region="Example SshCommand CreateCommand Execute" language="C#" title="Simple command execution" />
//         ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\SshCommandTest.cs" region="Example SshCommand CreateCommand Error" language="C#" title="Display command execution error" />
//         ///     <code source="..\..\src\Renci.SshNet.Tests\Classes\SshCommandTest.cs" region="Example SshCommand CreateCommand Execute CommandTimeout" language="C#" title="Specify command execution timeout" />
//         /// </example>
//         /// <exception cref="Renci.SshNet.Common.SshConnectionException">Client is not connected.</exception>
//         /// <exception cref="Renci.SshNet.Common.SshOperationTimeoutException">Operation has timed out.</exception>
//         public string Execute()
//         {
//            return EndExecute(BeginExecute(null, null));
//         }

//         /// <summary>
//         /// Cancels command execution in asynchronous scenarios. 
//         /// </summary>
//         public void CancelAsync()
//         {
//            if (_channel != null && _channel.IsOpen && _asyncResult != null)
//            {
//               // TODO: check with Oleg if we shouldn't dispose the channel and uninitialize it ?
//               _channel.Dispose();
//            }
//         }

//         /// <summary>
//         /// Executes the specified command text.
//         /// </summary>
//         /// <param name="commandText">The command text.</param>
//         /// <returns>Command execution result</returns>
//         /// <exception cref="Renci.SshNet.Common.SshConnectionException">Client is not connected.</exception>
//         /// <exception cref="Renci.SshNet.Common.SshOperationTimeoutException">Operation has timed out.</exception>
//         public string Execute(string commandText)
//         {
//            CommandText = commandText;

//            return Execute();
//         }

//         private IChannelSession CreateChannel()
//         {
//            var channel = _session.CreateChannelSession();
//            channel.DataReceived += Channel_DataReceived;
//            channel.ExtendedDataReceived += Channel_ExtendedDataReceived;
//            channel.RequestReceived += Channel_RequestReceived;
//            channel.Closed += Channel_Closed;
//            return channel;
//         }

//         private void Session_Disconnected(object sender, EventArgs e)
//         {
//            //  If objected is disposed or being disposed don't handle this event
//            if (_isDisposed)
//               return;

//            _exception = new SshConnectionException("An established connection was aborted by the software in your host machine.", DisconnectReason.ConnectionLost);

//            _sessionErrorOccuredWaitHandle.Set();
//         }

//         private void Session_ErrorOccured(object sender, ExceptionEventArgs e)
//         {
//            //  If objected is disposed or being disposed don't handle this event
//            if (_isDisposed)
//               return;

//            _exception = e.Exception;

//            _sessionErrorOccuredWaitHandle.Set();
//         }

//         private void Channel_Closed(object sender, ChannelEventArgs e)
//         {
//            var outputStream = OutputStream;
//            if (outputStream != null)
//            {
//               outputStream.Flush();
//            }

//            var extendedOutputStream = ExtendedOutputStream;
//            if (extendedOutputStream != null)
//            {
//               extendedOutputStream.Flush();
//            }

//            _asyncResult.IsCompleted = true;

//            if (_callback != null)
//            {
//               //  Execute callback on different thread
//               ExecuteThread(() => _callback(_asyncResult));
//            }
//            ((EventWaitHandle)_asyncResult.AsyncWaitHandle).Set();
//         }

//        public static void ExecuteThread(Action action)
//        {
//            if (action == null)
//                throw new ArgumentNullException("action");

//#if FEATURE_THREAD_THREADPOOL
//            System.Threading.ThreadPool.QueueUserWorkItem(o => action());
//#elif FEATURE_THREAD_TAP
//            System.Threading.Tasks.Task.Run(action);
//#else
//            #error Execution of action in a separate thread is not implemented.
//#endif
//        }
//         private void Channel_RequestReceived(object sender, ChannelRequestEventArgs e)
//         {
//            var exitStatusInfo = e.Info as ExitStatusRequestInfo;
//            if (exitStatusInfo != null)
//            {
//               ExitStatus = (int)exitStatusInfo.ExitStatus;

//               if (exitStatusInfo.WantReply)
//               {
//                  var replyMessage = new ChannelSuccessMessage(_channel.LocalChannelNumber);
//                  _session.SendMessage(replyMessage);
//               }
//            }
//            else
//            {
//               if (e.Info.WantReply)
//               {
//                  var replyMessage = new ChannelFailureMessage(_channel.LocalChannelNumber);
//                  _session.SendMessage(replyMessage);
//               }
//            }
//         }

//         private void Channel_ExtendedDataReceived(object sender, ChannelExtendedDataEventArgs e)
//         {
//            if (ExtendedOutputStream != null)
//            {
//               ExtendedOutputStream.Write(e.Data, 0, e.Data.Length);
//               ExtendedOutputStream.Flush();
//            }

//            if (e.DataTypeCode == 1)
//            {
//               _hasError = true;
//            }
//         }

//         private void Channel_DataReceived(object sender, ChannelDataEventArgs e)
//         {
//            if (OutputStream != null)
//            {
//               OutputStream.Write(e.Data, 0, e.Data.Length);
//               OutputStream.Flush();
//            }

//            if (_asyncResult != null)
//            {
//               lock (_asyncResult)
//               {
//                  _asyncResult.BytesReceived += e.Data.Length;
//               }
//            }
//         }

//         /// <exception cref="SshOperationTimeoutException">Command '{0}' has timed out.</exception>
//         /// <remarks>The actual command will be included in the exception message.</remarks>
//         private void WaitOnHandle(WaitHandle waitHandle)
//         {
//            var waitHandles = new[]
//                {
//                    _sessionErrorOccuredWaitHandle,
//                    waitHandle
//                };

//            switch (WaitHandle.WaitAny(waitHandles, CommandTimeout))
//            {
//               case 0:
//                  throw _exception;
//               case WaitHandle.WaitTimeout:
//                  throw new SshOperationTimeoutException(string.Format(CultureInfo.CurrentCulture, "Command '{0}' has timed out.", CommandText));
//            }
//         }

//         /// <summary>
//         /// Unsubscribes the current <see cref="SshCommand"/> from channel events, and disposes
//         /// the <see cref="IChannel"/>.
//         /// </summary>
//         /// <param name="channel">The channel.</param>
//         /// <remarks>
//         /// Does nothing when <paramref name="channel"/> is <c>null</c>.
//         /// </remarks>
//         private void UnsubscribeFromEventsAndDisposeChannel(IChannel channel)
//         {
//            if (channel == null)
//               return;

//            // unsubscribe from events as we do not want to be signaled should these get fired
//            // during the dispose of the channel
//            channel.DataReceived -= Channel_DataReceived;
//            channel.ExtendedDataReceived -= Channel_ExtendedDataReceived;
//            channel.RequestReceived -= Channel_RequestReceived;
//            channel.Closed -= Channel_Closed;

//            // actually dispose the channel
//            channel.Dispose();
//         }

//         #region IDisposable Members

//         private bool _isDisposed;

//         /// <summary>
//         /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
//         /// </summary>
//         public void Dispose()
//         {
//            Dispose(true);
//            GC.SuppressFinalize(this);
//         }

//         /// <summary>
//         /// Releases unmanaged and - optionally - managed resources
//         /// </summary>
//         /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
//         protected virtual void Dispose(bool disposing)
//         {
//            if (_isDisposed)
//               return;

//            if (disposing)
//            {
//               // unsubscribe from session events to ensure other objects that we're going to dispose
//               // are not accessed while disposing
//               var session = _session;
//               if (session != null)
//               {
//                  session.Disconnected -= Session_Disconnected;
//                  session.ErrorOccured -= Session_ErrorOccured;
//                  _session = null;
//               }

//               // unsubscribe from channel events to ensure other objects that we're going to dispose
//               // are not accessed while disposing
//               var channel = _channel;
//               if (channel != null)
//               {
//                  UnsubscribeFromEventsAndDisposeChannel(channel);
//                  _channel = null;
//               }

//               var outputStream = OutputStream;
//               if (outputStream != null)
//               {
//                  outputStream.Dispose();
//                  OutputStream = null;
//               }

//               var extendedOutputStream = ExtendedOutputStream;
//               if (extendedOutputStream != null)
//               {
//                  extendedOutputStream.Dispose();
//                  ExtendedOutputStream = null;
//               }

//               var sessionErrorOccuredWaitHandle = _sessionErrorOccuredWaitHandle;
//               if (sessionErrorOccuredWaitHandle != null)
//               {
//                  sessionErrorOccuredWaitHandle.Dispose();
//                  _sessionErrorOccuredWaitHandle = null;
//               }

//               _isDisposed = true;
//            }
//         }

//         /// <summary>
//         /// Releases unmanaged resources and performs other cleanup operations before the
//         /// <see cref="SshCommand"/> is reclaimed by garbage collection.
//         /// </summary>
//         ~SshCommandRaw()
//         {
//            Dispose(false);
//         }

//         #endregion
//      }
//}
